package com.bihua.iot.dynamicScript.pool;

import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;

import javax.script.CompiledScript;

import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import com.bihua.common.utils.StringUtils;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.DigestUtil;

/**
 * @author bihua
 * @date 2023年06月20日 10:10
 */
public class PoolUtil {
    private static ConcurrentHashMap<Long, Pair<String, Pool<CompiledScript>>> poolMaps = new ConcurrentHashMap();

    public static Pool<CompiledScript> getPool(Long id, String script){
        Pair<String, Pool<CompiledScript>> poolPair = poolMaps.compute(id, new BiFunction<Long, Pair<String, Pool<CompiledScript>>, Pair<String, Pool<CompiledScript>>>() {
            @Override
            public Pair<String, Pool<CompiledScript>> apply(Long aLong, Pair<String, Pool<CompiledScript>> stringPoolPair) {
                Pair<String, Pool<CompiledScript>> oldPoolPair = poolMaps.get(aLong);
                String newMd5 = DigestUtil.md5Hex(script);
                if(ObjectUtil.isNull(oldPoolPair)){
                    return  new Pair<>(newMd5, getCompiledScriptPool(script));
                }else{
                    if(StringUtils.equalsIgnoreCase(newMd5, oldPoolPair.getKey())){
                        return oldPoolPair;
                    }else{
                        oldPoolPair.getValue().destroy();
                        return new Pair<>(newMd5, getCompiledScriptPool(script));
                    }
                }
            }
        });
        return poolPair.getValue();
    }

    private static Pool<CompiledScript> getCompiledScriptPool(String script) {
        PooledObjectFactory<CompiledScript> pooledObjectFactory = new ScriptPooledObjectFactory("js", script);
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        //对象池的最大容量。池里最多存放多少个对象，默认8。
        poolConfig.setMaxTotal(8);
        //对象池中最少需要有几个空闲对象。
        //默认值为0，该值的有效范围n应该满足： 0 < n <= maxIdle
        poolConfig.setMinIdle(8);
        //对象池中最多可以有多少个空闲对象,默认8。
        poolConfig.setMaxIdle(8);
        //当连接池资源耗尽时,调用者最大阻塞的时间,超时时抛出异常 单位:毫秒数
        //默认值 -1，表示无限等待。
        poolConfig.setMaxWait(Duration.ofMillis(-1));
        //连接池存放池化对象方式,true放在空闲队列最前面,false放在空闲队列最后
        poolConfig.setLifo(true);
        //连接空闲的最小时间,达到此值后空闲连接可能会被移除,默认即为30分钟
        poolConfig.setMinEvictableIdleTime(Duration.ofMinutes(30));
        //连接耗尽时是否阻塞,默认为true
        poolConfig.setBlockWhenExhausted(true);
        //回收器线程多久执行一次空闲对象回收（轮询间隔时间，单位毫秒）。
        //默认-1，意味着不启动回收器线程。
        poolConfig.setTimeBetweenEvictionRuns(Duration.ofMinutes(30));

        return (Pool<CompiledScript>) new Pool(pooledObjectFactory,poolConfig);
    }
}
